/*******************************************************************************
 *  Copyright (c) [scl]。保留所有权利。
 *     本文仅供个人学习和研究使用，禁止用于商业用途。
 *     @brief ESP8266 模块的编写
 *     @author scl
 *     @note
 *          1. AT 指令必须⼤大写，并且以回⻋车换⾏行行符结尾（CR LF）
 *          2. 使用双引号表示字符串串数据 ”string”
 *          3. 默认波特率为 115200
 ******************************************************************************/

#ifndef STM32_LIB_MODULE_ESP8266_H
#define STM32_LIB_MODULE_ESP8266_H

#include "sys_core.h"

#define ESP_CLOSED_MSG "CLOSED\r\n"
typedef struct  // 串口数据帧的处理结构体
{
    char *p_rx_buf;
    uint16_t rx_buf_len;
    union {
        volatile uint16_t InfAll;
        struct { /*这里使用的是位段*/
            volatile uint16_t frame_len: 15;     // 14:0
            volatile uint16_t frame_finished_flag: 1; // 15
        } InfBit;
    };

} data_frame_t; /** esp8266 接收数据结构体*/
typedef enum {
    ap_mode = 0,    /* 设置 ESP8266 SoftAP*/
    sta_mode = 1,   /* 设置 ESP8266 Station*/
    sta_ap_mode = 2 /*设置 ESP8266 SoftAP 和 Station*/
} esp8266_net_mode;
typedef enum {
    open_mode = 0,
    wep_mode = 1,
    wpa_psk_mode = 2,
    wpa2_psk_mode = 3,
    wpa_wpa2_psk_mode = 4,
} esp8266_ap_psd_mode;
typedef enum {
    multiple_id_0 = 0,
    multiple_id_1 = 1,
    multiple_id_2 = 2,
    multiple_id_3 = 3,
    multiple_id_4 = 4,
    single_id_0 = 5, /*单连接模式*/
} esp8266_id_no;

typedef enum {
    link_error = 0,/*获取状态失败*/
    link_get_ip = 2, /*获取ip*/
    link_established = 3, /*建立连接*/
    link_lose_connect = 4, /*失去连接*/
} esp8266_link_status;

/**
 * @brief esp 延迟函数指针定义
 * */
typedef void (*esp_delay_def)(uint32_t ms);

/**
 * @brief  esp 调试函数指针定义
 */
typedef void (*esp_debug_def)(char *format, ...);
/*************************************************外部实现接口***************************************************/
/**
 * @brief esp8266 驱动外设初始化
 */
extern void esp8266_driver_init();

/**
 * @brief esp8266 复位引脚
 * @param flag true: 置高； false: 置低
 */
extern void esp8266_rst_pin(bool flag);

/**
 * @brief esp8266 使能引脚
 * @param flag true: 使能
 */
extern void esp8266_ch_enable(bool flag);


/**
 * @brief esp8266 发送数据
 * @param data
 * @param len
 */
extern void esp8266_send(void *data, uint16_t len);

/**
 * @brief 清空接收缓冲区
 */
extern void esp8266_clear_buf(void);

/**
 * @brief esp8266 接收数据
 * @return 接收数据
 */
extern char *esp8266_rec();

/**
 * @brief esp8266 构造指令的缓存区
 * @return
 */
extern char *esp8266_get_cmd_buf();
/*************************************************底层收发接口***************************************************/
/**
 * @brief esp8266发送和接收
 * @param data 数据
 * @param wait_time 等待响应的时间
 * @return 返回结果
 */
char *esp8266_send_and_rec(char *data, uint32_t wait_time);

/**
 * @brief esp8266发送和接收 并进行验证
 * @param cmd 数据
 * @param reply1 期待的响应1，为NULL表不需响应
 * @param reply2 期待的响应2，为NULL表不需响应
 *      reply1和reply2为或逻辑关系
 * @param wait_time
 * @return
 */
char *esp8266_send_cmd_msg(char *cmd, const char *reply1, const char *reply2, uint32_t wait_time);
/**
 * @brief 对WF-ESP8266模块发送AT指令
 * @param cmd 发送的指令
 * @param reply1 期待的响应1，为NULL表不需响应
 * @param reply2 期待的响应2，为NULL表不需响应
 *               reply1和reply2为或逻辑关系
 * @param wait_time 等待响应的时间
 * @return
 */
bool esp8266_send_cmd(char *cmd, const char *reply1, const char *reply2, uint32_t wait_time);
/**
 * @brief 对WF-ESP8266模块发送数据
 * @param data 发送的数据
 * @param len 数据长度
 * @param reply1 期待的响应1，为NULL表不需响应
 * @param reply2 期待的响应2，为NULL表不需响应
 *               reply1和reply2为或逻辑关系
 * @param wait_time 等待响应的时间
 * @return
 */
bool esp8266_send_data(uint8_t *data, uint16_t len, const char *reply1, const char *reply2, uint32_t wait_time);
/*************************************************函数回调接口***************************************************/
/**
 * @brief 设置esp8266 延迟函数回调
 * @param call
 */
void esp8266_set_delay_call(esp_delay_def call);

/**
 * @brief 设置esp8266 调试函数回调
 * @param call
 */
void esp8266_set_debug_call(esp_debug_def call);
/*************************************************基础接口***************************************************/
/**
 * @brief 对WF-ESP8266模块进行AT测试启动
 * @param try_cnt 尝试次数
 * @param wait_time 设置esp8266 延迟函数回调
 * @return
 */
bool esp8266_base_at_cmd_test(uint8_t try_cnt, uint32_t wait_time);
/**
 * @brief 开关回显功能
 * @param en
 *      true:  开回显
 *      false: 关回显
 * @param wait_time 设置esp8266 延迟函数回调(默认 200)
 */
bool esp8266_base_ate_conf(bool en, uint32_t wait_time);
/**
 * @brief 恢复出厂设置
 * @param wait_time 设置esp8266 延迟函数回调(默认 200)
 */
bool esp8266_base_factory_reset_conf(uint32_t wait_time);

/**
 * @brief 重启WF-ESP8266模块
 * @param wait_time 等待响应的时间 (-1,默认为 500 )
 */
void esp8266_base_rst(bool soft, uint32_t wait_time);
/****************************************ESP8266 WIFI 配置**************************************************/
/**
 * @brief 设置当前 Wi-Fi 模式，@note 不保存到 Flash
 * @param mode
 *          ap_mode     设置 ESP8266 SoftAP
 *          sta_mode    设置 ESP8266 Station
 *          sta_ap_mode 设置 ESP8266 SoftAP 和 Station
 * @param wait_time 等待响应的时间 (-1,默认为 100 )
 * @return
 */
bool esp8266_wifi_mode_cur_set(esp8266_net_mode mode, int wait_time);
/**
 * @brief 临时连接 AP
 *  @note
 *      1. 参数设置需要开启 Station 模式,相关配置请查看 @see esp8266_wifi_mode_cur_set
 *      2. 若 SSID 或者password 中含有特殊符号，例如 , 或者 “ 或者 \ 时，需要进行转义，其它字符转义无效。
 * @param ssid WiFi名称字符串
 * @param pwd WiFi密码字符串
 * @param bssid 目标 AP 的 MAC 地址(可选配置)
 * @param wait_time 等待响应的时间 (-1,默认为 3000 )
 * @return
 */
bool esp8266_wifi_join_cur_set(char *ssid, char *pwd, char *bssid, int wait_time);
/**
 * @brief 断开与 AP 的连接
 * @param wait_time 等待响应的时间 (-1,默认为 100 )
 * @return
 */
bool esp8266_wifi_disconnect(int wait_time);

/**
 * @brief 配置 ESP8266 SoftAP 当前参数
 *  @note 指令只有在 SoftAP 模式开启后有效，相关配置请查看 @see esp8266_wifi_mode_cur_set
 * @param ssid WiFi名称字符串
 * @param pwd WiFi密码字符串
 * @param channel 通道号
 * @param psd_mode 加密方式，不支持 WEP
 * @param wait_time 等待响应的时间 (-1,默认为 100 )
 * @return
 */
bool esp8266_wifi_conf_cur_set(char *ssid, char *pwd, uint8_t channel,
                               esp8266_ap_psd_mode psd_mode, int wait_time);

/****************************************ESP8266 DHCP 配置**************************************************/

/**
 * @brief 设置 DHCP,不保存到 Flash
 * @note
 *      1. 本设置指令与设置静态 IP 的指令（AT+CIPSTA 系列列和 AT+CIPAP 系列）互相影响
 *         > 设置使能 DHCP，则静态 IP 无效
 *         > 设置静态 IP，则 DHCP 关闭
 *         > 以最后的设置为准
 * @param mode
 *          ap_mode     设置 ESP8266 SoftAP
 *          sta_mode    设置 ESP8266 Station
 *          sta_ap_mode 设置 ESP8266 SoftAP 和 Station
 * @param en
 *          true:  开启DHCP
 *          false: 关闭DHCP
 * @param wait_time 等待响应的时间 (-1,默认为 500 )
 * @return
 */
bool esp8266_dhcp_cur_set(esp8266_net_mode mode, bool en, int wait_time);
/**
 * @brief 设置 ESP8266 SoftAP DHCP 分配的 IP 范围，不不保存到 Flash
 *  @note
 *      1. 本指令必须在 ESP8266 SoftAP 模式使能， @see esp8266_wifi_mode_cur_set
 *      2. 且开启 DHCP 的情况下使用， @see esp8266_dhcp_cur_set
 *      3. 设置的 IP 范围必须与 ESP8266
SoftAP 在同⼀一⽹网段。
 * @param en
 *      false: 清除设置 IP 范围，恢复默认值，后续参数无需填写
 *      true: 使能设置 IP 范围，后续参数必须填写
 * @param lease_min 租约时间，单位：分钟，取值范围 [1, 2880]
 * @param start_ip DHCP 服务器 IP 池的起始 IP
 * @param end_ip   DHCP 服务器 IP 池的结束 IP
 * @param wait_time 等待响应的时间 (-1,默认为 500 )
 * @return
 */
bool esp8266_dhcp_ip_cur_set(bool en, uint16_t lease_min, char *start_ip, char *end_ip, int wait_time);
/****************************************ESP8266 MAC地址 配置**************************************************/
/**
 * @brief 设置 ESP8266 Station 当前 MAC 地址，不保存到 Flash
 *  @note
 *      1. ESP8266 SoftAP 和 Station 的 MAC 地址并不相同，请勿将其设置为同一 MAC 地址。
 *      2. ESP8266 MAC 地址第一个字节的 bit 0 不不能为 1，例如，MAC 地址可以为 "18:…" 但不不能为 "15:…"。
 * @param mac mac地址
 * @param wait_time 等待响应的时间 (-1,默认为 100 )
 * @return
 */
bool esp8266_mac_sta_cur_set(char *mac, int wait_time);
/**
 * @brief 设置 ESP8266 SoftAP 当前 MAC 地址，不保存到 Flash
 *  @note
 *      1. ESP8266 SoftAP 和 Station 的 MAC 地址并不相同，请勿将其设置为同一 MAC 地址。
 *      2. ESP8266 MAC 地址第一个字节的 bit 0 不不能为 1，例如，MAC 地址可以为 "18:…" 但不不能为 "15:…"。
 * @param mac mac地址
 * @param wait_time 等待响应的时间 (-1,默认为 100 )
 * @return
 */
bool esp8266_mac_ap_cur_set(char *mac, int wait_time);

/****************************************ESP8266 IP地址 配置**************************************************/
/**
 * @brief 设置 ESP8266 Station 的 IP 地址，不不保存到 Flash
 *  @note
 *      1. 本设置指令与设置 DHCP 的指令（AT+CWDHCP 系列）互相影响： @see esp8266_dhcp_cur_set
 *          ‣ 设置静态 IP，则 DHCP 关闭；
 *          ‣ 设置使能 DHCP，则静态 IP 无效；
 *          ‣ 以最后的设置为准。
 *      2. 目前仅支持 C 类 IP 地址
 * @param ip 字符串，
 * @param gw 网关
 * @param sub 子⽹网掩码
 * @param wait_time  等待响应的时间 (-1,默认为 500 )
 * @return
 */
bool esp8266_ip_sta_cur_set(char *ip, char *gw, char *sub, int wait_time);
/**
 * @brief 设置 ESP8266 SoftAP 的 IP 地址，不保存到 Flash
 *  @note
 *      1. 本设置指令与设置 DHCP 的指令（AT+CWDHCP 系列）互相影响： @see esp8266_dhcp_cur_set
 *          ‣ 设置静态 IP，则 DHCP 关闭；
 *          ‣ 设置使能 DHCP，则静态 IP 无效；
 *          ‣ 以最后的设置为准。
 *      2. 目前仅支持 C 类 IP 地址
 * @param ip 字符串，
 * @param gw 网关
 * @param sub 子网掩码
 * @param wait_time  等待响应的时间 (-1,默认为 500 )
 * @return
 */
bool esp8266_ip_ap_cur_set(char *ip, char *gw, char *sub, int wait_time);

/**
 * @brief 获取 F-ESP8266 的 AP IP
 *  @note ESP8266 Station IP 需连上 AP 后，才可以查询
 * @param ret_ip 存放 AP IP 的数组的首地址
 * @param len 数组的长度
 * @param wait_time 等待响应的时间 (-1,默认为 500 )
 * @return
 */
bool esp8266_ip_ap_inquire(char *ret_ip, uint8_t len, int wait_time);


/**
 * @brief 查询本地 IP 地址
 *  @note
 *      1. ESP8266 Station IP 需连上 AP 后，才可以查询。
 * @param dstIp 保存查询结果
 * @param len dstIp的长度
 * @param wait_time 等待响应的时间 (-1,默认为 100 )
 * @return
 */
bool esp8266_ip_sta_inquire(char *dstIp, uint8_t len, int wait_time);
/****************************************ESP8266 功能指令**************************************************/
/**
 * @brief 获取 WF-ESP8266 的连接状态，较适合单端口时使用
 * @param wait_time 等待响应的时间 (-1,默认为 500 )
 * @return
 */
esp8266_link_status esp8266_net_link_status(int wait_time);
/**
 * @brief 建立 TCP 连接，UDP 传输或 SSL 连接(单连接)
 * @param type
 *      1: tcp
 *      2: udp
 *      3: ssl
 * @param server_addr 服务器地址
 * @param port 服务器端口
 * @param wait_time 等待响应的时间 (-1,默认为 500 )
 * @return
 */
bool esp8266_net_single_socket_build(uint8_t type, char *server_addr, uint16_t port, int wait_time);
/**
 * @brief  建立 TCP 连接，UDP 传输或 SSL 连接(多连接)
 * @param type
 *      1: tcp
 *      2: udp
 *      3: ssl
 * @param link_id 网络连接 ID (0 ~ 4)，用于多连接的情况
 * @param server_addr 服务器地址
 * @param port 服务器端口
 * @param wait_time 等待响应的时间 (-1,默认为 500 )
 * @return
 */
bool esp8266_net_mul_socket_build(uint8_t type, uint8_t link_id, char *server_addr, uint16_t port, int wait_time);
/**
 * @brief 关闭 TCP/UDP/SSL 传输
 *      AT+CIPCLOSE=<link	ID>
 *      @note
 *          需要关闭的连接 ID 号。当 ID 为 5 时，关闭所有连接。（开启 server 后 ID 为 5 无效）
 * @param id
 * @param wait_time 等待响应的时间 (-1,默认为 100 )
 * @return
 */
bool esp8266_socket_close(esp8266_id_no id, int wait_time);

/*************************************************ESP8266 服务器和客户端通用***************************************************/
/**
 * @brief WF-ESP8266模块启动多连接
 *      @note
 *          1. 默认为单连接；
 *          2. 只有⾮透传模式 (AT+CIPMODE=0)，才能设置为多连接
 *          3. 必须在没有连接建⽴的情况下，设置连接模式；
 *          4. 如果建⽴了 TCP 服务器，想切换为单连接，必须关闭服务器 (AT+CIPSERVER=0)，服务器仅⽀持多连接
 * @param en true: 多连接模式；false 单连接模式
 * @param wait_time 等待响应的时间 (-1,默认为 500 )
 * @return
 */
bool esp8266_mul_connect_set(bool en, int wait_time);
/*************************************************ESP8266 服务端***************************************************/
/**
 * @brief 设置服务器允许建⽴立的最大连接数。
 *  @note
 *      1.如需设置最⼤大连接数，请在创建服务器之前设置。
 * @param mx_conn 连接数(范围是 1-5)
 * @param wait_time 等待响应的时间 (-1,默认为 100 )
 * @return
 */
bool esp8266_server_connect_num_set(uint8_t mx_conn, int wait_time);
/**
 * @brief 设置 TCP 服务器超时时间
 *     @note
 *          1. ESP8266 作为 TCP 服务器，会断开一直不通信直至超时了的 TCP 客户端连接
 *          2. 如果设置 AT+CIPSTO=0，则永远不会超时，不建议这样设置
 * @param timeout TCP 服务器超时时间，取值范围 0 ~ 7200s
 * @param wait_time 等待响应的时间 (-1,默认为 100 )
 * @return
 */
bool esp8266_server_tcp_timeout_set(uint16_t timeout, int wait_time);
/**
 * @brief tcp 服务器建立
 *  @note
 *      1. 多连接情况下 (AT+CIPMUX=1)，才能开启 TCP 服务器。
 *      2. 创建 TCP 服务器后，自动建立 TCP 服务器监听。
 *      3. 当有 TCP 客户端接⼊入，会自动占用一个连接 ID。
 * @param en
 *      true:   开启服务器
 *      false: 关闭服务器
 * @param port 端口
 * @param wait_time 等待响应的时间 (-1,默认为 100 )
 * @return
 */
bool esp8266_server_tcp_start(bool en, uint16_t port, int wait_time);
/**
 * @brief 普通模式下发送数据
 * @param ucId 客户端连接id号
 * @param data 发送的字符串
 * @param len 发送的字符串的长度
 * @param wait_time 等待响应的时间 (-1,默认为 100 )
 * @return
 */
bool esp8266_server_tcp_send_to(esp8266_id_no ucId, char *data, uint16_t len, int wait_time);
/**
 * @brief 普通模式下发送字节数组数据
 * @param ucId 客户端连接id号
 * @param data 发送的字符串
 * @param len 发送的字符串的长度
 * @param wait_time 等待响应的时间 (-1,默认为 100 )
 * @return
 */
bool esp8266_server_tpc_send_bytes(esp8266_id_no ucId, uint8_t *data, uint16_t len, int wait_time);
/*************************************************ESP8266 客户端***************************************************/
/**
 * @brief 设置传输模式
 *          AT+CIPMODE=<mode>
 * @param en true: 透传模式；false 普通模式
 *      @note
 *          1. 透传模式，仅⽀支持 TCP 单连接和 UDP 固定通信对端的情况
 *          2. 本设置不不保存到 Flash。
 *          3. 透传模式传输时，如果连接断开， ESP8266 会不不停尝试重连，此时单独输⼊入 +++ 退出透传，则停⽌止重连；
 *             普通传输模式则不会重连，提示连接断开。
 *
 * @param wait_time 等待响应的时间 (-1,默认为 500 )
 * @return
 */
bool esp8266_transmission_mode_enter(bool en, int wait_time);

/**
 * @brief 退出透传模式
 *   @note
 *      1. 仅当传输模式为透传时
 * @param wait_time 等待响应的时间 (-1,默认为 500 )
 * @return
 */
void esp8266_transmission_mode_exit(int wait_time);

/**
 * @brief 在tcp 客户端 透传模式下，更新连接关闭状态标志为
 * @param close_status
 *          true: 已关闭连接
 *          false: 处于连接中
 */
void esp8266_tcp_update_closed_flag(bool close_status);
/**
 * @brief 在tcp 客户端 透传模式下，读取连接关闭状态标志为
 * @return
 *          true: 已关闭连接
 *          false: 处于连接中
 */
bool esp8266_tcp_read_closed_flag();

/**
 * @brief 透传模式下发送数据
 * @param str 数据
 * @param wait_time 等待响应的时间 (-1,默认为 100 )
 * @return 如果对端不反数据，则为NULL
 */
char *esp8266_transmission_mode_send(char *str, int wait_time);
/**
 * @brief WF-ESP8266模块发送字符串
 * @param ucId 哪个ID发送的字符串
 * @param already_tm_flag 是否已使能了透传模式
 * @param str 要发送的字符串
 * @param len 字符串的字节数
 * @param wait_time 等待响应的时间 (-1,默认为 1000 )
 * @return
 */
bool esp8266_send_str(esp8266_id_no ucId, bool already_tm_flag, char *str, uint32_t len, int wait_time);

#endif //STM32_LIB_MODULE_ESP8266_H
